//
// Created by chizuru on 2025/9/2.
//

#pragma once
#include <tinyxml2.h>
#include <iostream>
#include <tinyxml2.h>
#include <string>
#include <vector>
using namespace tinyxml2;

class RegBuilder {
public:
    // camerlink bobcat register
    struct EntryCLB {
        std::string category;
        std::string name;
        std::string address;
        std::string tooltip;
        std::string description;
    };

    // serial port temp-press-sensor
    struct EntryTPS {
        std::string category;
        std::string name;
        std::string address;
        std::string numOfReg;
        std::string tooltip;
        std::string description;
    };

    RegBuilder() {
        // 初始化文档
        // XMLDeclaration: 可按需生成
        auto decl = doc.NewDeclaration(R"(xml version="1.0" encoding="UTF-8")");
        doc.InsertFirstChild(decl);

        root = doc.NewElement("Registers");
        doc.InsertEndChild(root);
    }

    void addEntryCLB(const EntryCLB& e) {
        EntryCLB en = e;
        tinyxml2::XMLElement* reg = doc.NewElement("Register");
        reg->SetAttribute("category", en.category.c_str());
        reg->SetAttribute("name", en.name.c_str());

        if (!en.address.empty()) {
            auto addr = doc.NewElement("Address");
            addr->SetText(en.address.c_str());
            reg->InsertEndChild(addr);
        }
        if (!en.tooltip.empty()) {
            auto tip = doc.NewElement("ToolTip");
            tip->SetText(en.tooltip.c_str());
            reg->InsertEndChild(tip);
        }
        if (!en.description.empty()) {
            auto desc = doc.NewElement("Description");
            desc->SetText(en.description.c_str());
            reg->InsertEndChild(desc);
        }

        root->InsertEndChild(reg);
    }

    void addEntryTP(const EntryTPS& e) {
        EntryTPS en = e;
        tinyxml2::XMLElement* reg = doc.NewElement("Register");
        reg->SetAttribute("category", en.category.c_str());
        reg->SetAttribute("name", en.name.c_str());
        if (!en.address.empty()) {
            auto addr = doc.NewElement("Address");
            addr->SetText(en.address.c_str());
            reg->InsertEndChild(addr);
        }
        if (!en.numOfReg.empty()) {
            auto num = doc.NewElement("NumOfReg");
            num->SetText(en.numOfReg.c_str());
            reg->InsertEndChild(num);
        }
        if (!en.tooltip.empty()) {
            auto tip = doc.NewElement("ToolTip");
            tip->SetText(en.tooltip.c_str());
            reg->InsertEndChild(tip);
        }
        if (!en.description.empty()) {
            auto desc = doc.NewElement("Description");
            desc->SetText(en.description.c_str());
            reg->InsertEndChild(desc);
        }
        root->InsertEndChild(reg);
    }

    bool save_to_file(const std::string& filepath){
        tinyxml2::XMLError e = doc.SaveFile(filepath.c_str());
        if (e != tinyxml2::XML_SUCCESS) {
            std::cerr << "Failed to write XML: " << e << "\n";
            return false;
        }
        return true;
    }

private:
    tinyxml2::XMLDocument doc;
    tinyxml2::XMLElement* root = nullptr;
    std::vector<EntryCLB> entries;
};


int build_bobocat_reg() {
    RegBuilder builder;
    std::vector<RegBuilder::EntryCLB> entries = {
            { "SavingAndRestoring", "BootFrom", "0x6000", "", "" },
            { "SavingAndRestoring", "LoadFromFactory", "0x6060", "", "" },
            { "SavingAndRestoring", "RS-232BaudRateSelector", "0x0604", "", "" },

            { "CameraInformation", "TestRegister", "0x600C", "", "" },
            { "CameraInformation", "SoftReset", "0x601C", "", "" },
            { "CameraInformation", "HorizontalFrameSize", "0x6080", "", "" },
            { "CameraInformation", "VerticalFrameSize ", "0x6084", "", "" },
            { "CameraInformation", "CurrentMinimumFrameTime", "0x6088", "", "" },
            { "CameraInformation", "CurrentMaximumExposure", "0x6090", "", "" },
            { "CameraInformation", "CurrentMinimumExposure", "0x6094", "", "" },

            { "ExposureControl", "ExposureControlMode", "0x0544", "", "" },
            { "ExposureControl", "ExposureTime", "0x0548", "", "" },
            { "ExposureControl", "Prog.FrameTimeEnable", "0x0550", "", "" },
            { "ExposureControl", "FrameTime", "0x0558", "", "" },

            { "Video", "AnalogGainCh.1", "0x0004", "", "" },
            { "Video", "AnalogGainCh.2", "0x0014", "", "" },
            { "Video", "AnalogGainCh.3", "0x0024", "", "" },
            { "Video", "AnalogGainCh.4", "0x0034", "", "" },

            { "Trigger", "TriggerInputSelector", "0x0508", "", "" },
            { "Trigger", "TriggerEdgeSelector", "0x050C", "", "" },
            { "Trigger", "De-bounceTimeSelector", "0x0510", "", "" },
            { "Trigger", "TriggerOverlap", "0x0514", "", "" },
            { "Trigger", "TriggeringModeSelector", "0x0518", "", "" },
            { "Trigger", "TriggeringDisable", "0x05BC", "", "" },

            { "OutputDataFormat", "BitDepthSelector", "0x0100", "", "" },
            { "OutputDataFormat", "TapModeSelector", "0x0108", "", "" },
            { "OutputDataFormat", "DataFormatSelector ", "0x010C", "", "" },
            { "OutputDataFormat", "CameraSpeed", "0x0608", "", "" },

            { "ManufacturingData", "AssemblySerialNumber1", "0x7014", "", "" },
            { "ManufacturingData", "AssemblySerialNumber2", "0x7018", "", "" },
            { "ManufacturingData", "CameraType1", "0x702C", "", "" },
            { "ManufacturingData", "CameraType2", "0x7030", "", "" },
            { "ManufacturingData", "CameraType3", "0x7034", "", "" },
            { "ManufacturingData", "CameraType4", "0x7038", "", "" }
    };
    for (int i = 0; i < entries.size(); i++) {
        builder.addEntryCLB(entries[i]);
    }
    if (builder.save_to_file("bobcat_regs_generated.xml")) {
        return 0;
    }
    return 1;
}


int build_sensortp_reg() {
    RegBuilder builder;
    std::vector<RegBuilder::EntryTPS> entries = {
            { "UserDataSegment", "DeviceAddress", "0x0300", "1", "" , ""},
            { "UserDataSegment", "BaudRate", "0x0301", "2", "" , ""},
            { "UserDataSegment", "ParityCheck", "0x0303", "1", "" , ""},
            { "UserDataSegment", "PressureDecimalPoint", "0x0304", "1", "" , ""},
            { "UserDataSegment", "TemperatureDecimalPoint", "0x0305", "1", "" , ""},
            { "UserDataSegment", "PressureGainCoefficient", "0x0306", "2", "" , ""},
            { "UserDataSegment", "PressureBaseValue", "0x0308", "2", "" , ""},
            { "UserDataSegment", "TemperatureBaseValue", "0x030A", "2", "" , ""},
            { "UserDataSegment", "PressureUnit", "0x030C", "1", "" , ""},
            { "UserDataSegment", "TemperatureUnit", "0x030D", "1", "" , ""},

            { "ReadOnlyDataSegment", "FeatureCode", "0x0400", "1", "" , ""},
            { "ReadOnlyDataSegment", "SoftwareVersion", "0x0401", "1", "" , ""},
            { "ReadOnlyDataSegment", "PressureValue", "0x0404", "2", "" , ""},
            { "ReadOnlyDataSegment", "TemperatureValue", "0x0408", "2", "" , ""},
    };
    for (int i = 0; i < entries.size(); i++) {
        builder.addEntryTP(entries[i]);
    }
    if (builder.save_to_file("tp_sensor_regs_generated.xml")) {
        return 0;
    }
    return 1;
}